package YAPserver.logic;

import java.util.ArrayList;
import java.util.HashMap;
//import java.util.Random;

import YAPenvironment.YAPconstant;
import YAPserver.collisionEngine.Collision;
import YAPserver.collisionEngine.*;
import YAPserver.gameObjects.AbstractBall;
import YAPserver.gameObjects.GameBall;
import YAPserver.gameObjects.AbstractGameObject;
import YAPserver.gameObjects.Mod;
import YAPserver.gameObjects.Slider;
import YAPserver.gameObjects.GameWall;

/**
 * This is the standard game model. The implemented methods should be used by all game models.
 * 
 * @author Daniel
 *
 */

public abstract class AbstractModel implements GameModel {

	//TODO: implement all constants that come up in the game in the Model
	static int i=0;
	
	protected int m_ticks;
	protected int m_maxMods;
	
	protected double m_ballSpeed; //by how much one should increment the speed each round
	protected double m_modSpeed;
	
	public double defaultBallSpeed;
	public double defaultBallRadius;
	public double defaultModSpeed;
	public double defaultModRadius;
	
	protected Output out;
	
	/**
	 * Resets the number of ticks it will take for a mod to appear.
	 */
	abstract void resetTicks();
	
	@Override
	public void addMod(ArrayList<Mod> modsMoving, GameModifier gameMod) {
		if(m_ticks <= 0){
			resetTicks();
			//this limits the maximum number of mods allowed on the field
			if(modsMoving.size() < m_maxMods){
				createMod(modsMoving, gameMod);
			}
		}else{
			m_ticks = m_ticks - 1;
		}
	}
	
	@Override
	public void assignPlayers(HashMap<Integer, Player> players, HashMap<Integer, Integer> playerSliders,
			ArrayList<Slider> sliders, ArrayList<GameWall> walls){
		//default
		int sliderId = 0;
		int wallId;
		for(int id : players.keySet()){
			//first create sliders
			//then assign the walls and sliders to the player
			wallId = playerSliders.get(id);
			sliders.add(new Slider(walls.get(wallId)));
			players.get(id).setWallAndSlider(walls.get(wallId),sliders.get(sliderId));
			sliderId++;
		}
	}
	
	@Override
	public void assignPlayers(HashMap<Integer, Player> players,
			ArrayList<Slider> sliders, ArrayList<GameWall> walls) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean ballCollisions(AbstractBall ball, ArrayList<? extends AbstractGameObject> objects) {
		for(int k=0; k<objects.size(); k++){
//			if(ball.hasCollided((AbstractGameObject) objects.get(k))){
			if(Collision.detect(ball, (Wall) objects.get(k))){
				objects.get(k).hasBeenHit();				//the object registers it's been hit
				objects.get(k).notifyObservers(ball);	//it now tells all of its observers it's been hit
				if(ball.isOnField()){
					out.sendBounce(ball);
					ball.bounce((Wall) objects.get(k));
				}
				
				return true;
			}
		}
		return false;

	}

	@Override
	public void checkCollisions(ArrayList<GameBall> balls,
			ArrayList<Mod> modsMoving, ArrayList<GameWall> walls,
			ArrayList<Slider> sliders) {
		for(int i=0; i<balls.size(); i++){
			//first test collisions with sliders, and if there weren't any, test with walls
			if(ballCollisions(balls.get(i), sliders) == false){
				ballCollisions(balls.get(i), walls);
			}
		}
		for(int i=0; i<modsMoving.size(); i++){
			//first test collisions with sliders, and if there weren't any, test with walls
			if(ballCollisions(modsMoving.get(i), sliders) == false){
				ballCollisions(modsMoving.get(i), walls);
			}
		}
	}

	@Override
	public void deleteBall(ArrayList<GameBall> balls, ArrayList<Mod> mods) {
		for(int i=0; i<balls.size(); i++){
			if(balls.get(i).isOnField() == false){
				balls.remove(i);
			}else{
				//TODO: get rid of magic numbers
				//if the ball is outside of the screen then delete
				if(balls.get(i).getMiddlePoint().getXPos() < 0 - 50
						|| balls.get(i).getMiddlePoint().getXPos() > 600 + 50
						|| balls.get(i).getMiddlePoint().getYPos() < 0 - 50
						|| balls.get(i).getMiddlePoint().getYPos() > 600 + 50){
					balls.remove(i);
				}
			}
		}
		for(int i=0; i<mods.size(); i++){
			if(mods.get(i).isOnField() == false){
				mods.remove(i);
			}else if(mods.get(i).getMiddlePoint().getXPos() < 0 - 50
					|| mods.get(i).getMiddlePoint().getXPos() > 600 + 50
					|| mods.get(i).getMiddlePoint().getYPos() < 0 - 50
					|| mods.get(i).getMiddlePoint().getYPos() > 600 + 50){
				mods.remove(i);
			}
		}
	}

	@Override
	public void deletePlayer(HashMap<Integer, Player> players,
			ArrayList<Slider> sliders, Input in, GamePlay gamePlay) {
//		ArrayList<Integer> idsToRemove = new ArrayList<Integer>();
		//first note the ids that need removing
		int alive = 0;
		for(int id : players.keySet()){
			if(players.get(id).getLives() < YAPconstant.C_MINLIVES){
				players.get(id).stopObserving();
				sliders.remove(players.get(id).getSlider());
				in.getCommands().remove(id);
			}else{
				alive++;
			}
		}
		gamePlay.setPlayersAlive(alive);
			return;
	}

	@Override
	public void moveSliders(HashMap<Integer, Player> players) {
		for(int id : players.keySet()){
			players.get(id).move();
		}
	}

	@Override
	public void moveBalls(ArrayList<GameBall> balls, ArrayList<Mod> modsMoving) {
		for(int i=0; i<balls.size(); i++){
			balls.get(i).move();
		}
		for(int i=0; i<modsMoving.size(); i++){
			modsMoving.get(i).move();
		}
	}

	@Override
	public void tickActiveMods(ArrayList<Mod> modsActive) {
		for(int i=0; i<modsActive.size(); i++){
			modsActive.get(i).tick();
		}
	}

	public void setNumberOfMods(int i){
		m_maxMods = i;
	}
	
	public void setBallIncrementSpeed(double speed){
		m_ballSpeed = speed;
	}
	public void setModIncrementSpeed(double speed){
		m_modSpeed = speed;
	}
	/**
	 * Sets the Output which is used to send the signal for playing various sounds on the client side.
	 * @param out
	 */
	public void setOutput(Output out){
		this.out = out;
	}
}
